#pragma once

#include <iostream>
#include <sys/epoll.h>
#include "Socket.hpp"
#include "epoller.hpp"
#include "log.hpp"
#include<memory>

uint32_t EventIn = EPOLLIN;
uint32_t EventOut = EPOLLOUT;
int num = 128;

class EpollServer
{
public:
    EpollServer(uint16_t port) : port_(port), epoller_ptr(new Epoller(3000))
    {
    }

    void Init()
    {
        listensock_.Socket();
        listensock_.Bind(port_);
        listensock_.Listen();
        lg(Info, "create listen socket success: %d\n", listensock_.GetSockfd());
    }

    void Accept(int listensock)
    {
        std::string client_ip;
        uint16_t client_port;
        int socket = listensock_.Accept(&client_ip, &client_port);
        if (socket < 0)
            return;
        lg(Info, "get a new link,client info: %s@%d, fd is %d", client_ip.c_str(),client_port,socket);
        epoller_ptr->EpollerUpdate(EPOLL_CTL_ADD,socket,EventIn);
    }


    void Dispatcher(struct epoll_event events[],int num)
    {
        for (int i = 0; i < num; i++)
        {
            int fd = events[i].data.fd;
            uint32_t event = events[i].events;
            if (event & EventIn)
            {
                if (fd == listensock_.GetSockfd())
                {
                    Accept(fd);
                }
                else
                {
                    Recv(fd);
                }
            }
        }
    }

    void Recv(int fd)
    {
        char buffer[128];
        int n = read(fd, buffer, sizeof(buffer) - 1);
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << "receive a message: " << buffer << std::endl;

            std::string outputstring("server echo@ ");
            outputstring += buffer;
            write(fd,outputstring.c_str(),outputstring.size());
        }
        else if (n == 0)
        {
            lg(Info, "client close the socket,server close too!!!,fd is: %d", fd);
            epoller_ptr->EpollerUpdate(EPOLL_CTL_DEL,fd,0);
            close(fd);
        }
        else
        {
            lg(Warning, "receive message error,fd is: %d", fd);
            epoller_ptr->EpollerUpdate(EPOLL_CTL_DEL,fd,0);
            close(fd);
        }
    }

    void Start()
    {
        epoller_ptr->EpollerUpdate(EPOLL_CTL_ADD, listensock_.GetSockfd(), EventIn);
        struct epoll_event revents[num];
        for (;;)
        {

            int n = epoller_ptr->EpollerWait(revents, num);
            if (n > 0)
            {
                lg(Debug, "event happened, fd is : %d", revents[0].data.fd);
                Dispatcher(revents,n);
            }
            else if (n == 0)
            {
                lg(Info, "time out....");
            }
        }
    }
    EpollServer()
    {
        listensock_.Close();
    }

private:
    uint16_t port_;
    Sock listensock_;
    std::shared_ptr<Epoller> epoller_ptr;
};